package com.powernode.aspect;

import com.alibaba.fastjson.JSON;
import com.powernode.utils.ServletUtil;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.Duration;

/**
 * @Author: powernode
 */
@Component
@Aspect
public class IdemAspect {

    @Autowired
    private StringRedisTemplate redisTemplate;

    /**
     * 接口幂等的环绕通知
     * 将请求ip，方法名，参数等作为唯一标记
     * 使用redis的setnx命令做业务控制
     *
     * @param joinPoint
     * @return
     */
    @Around(value = "@annotation(com.powernode.anno.Idempotence)")
    public Object IdemAround(ProceedingJoinPoint joinPoint) {
        // 拿到参数以及方法名称和ip
        String ip = ServletUtil.getIp();
        // 获取参数
        Object[] args = joinPoint.getArgs();
        String argStr = JSON.toJSONString(args);
        // 获取方法的全限定类名
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();
        // 获取类名
        Class<?> aClass = method.getDeclaringClass();
        String typeName = aClass.getTypeName();
        // 构建唯一的标识
        String uniqueId = ip + ":" + typeName + "." + methodName + ":" + argStr;
        // 使用setnx方式 并且设置过期时间1s 根据业务来控制 例如 1s内不能出现多次相同的参数请求
        Boolean flag = redisTemplate.opsForValue().setIfAbsent("Idempotence:" + uniqueId, "", Duration.ofSeconds(1));
        if (flag) {
            // 执行目标方法
            try {
                return joinPoint.proceed(args);
            } catch (Throwable throwable) {
                throwable.printStackTrace();
            }
        }
        // 走到这里说明有重复请求
        return "重复请求";

    }


}
